package pl.lodz.p.kipme.dao;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Query;
import org.springframework.stereotype.Repository;

import pl.lodz.p.kipme.bo.Category;
import pl.lodz.p.kipme.bo.Product;
import pl.lodz.p.kipme.bo.ProductInfo;
import pl.lodz.p.kipme.bo.ProductType;
import pl.lodz.p.kipme.entities.CategoryEntity;
import pl.lodz.p.kipme.entities.ProductEntity;
import pl.lodz.p.kipme.entities.TypeEntity;
import pl.lodz.p.kipme.exception.BusinessException;
import pl.lodz.p.kipme.util.ProductUtil;

@Repository("productDAO")
public class ProductDAOImpl extends BasicDaoSupport implements ProductDAO {

	@Override
	public void addProduct(Product product) throws BusinessException {
		ProductEntity existingEntity = getProductEntityByCode(product
				.getProductCode());
		if (existingEntity != null) {
			throw new BusinessException("Product with code: "
					+ product.getProductCode() + " already exists in DB");
		}
		session.beginTransaction();
		CategoryEntity categoryEntity = getCategoryEntityByCode(product
				.getCategory().getCategoryCode());
		TypeEntity typeEntity = getTypeEntityByCode(product.getType()
				.getTypeCode());
		ProductEntity entity = ProductUtil.product2ProductEntity(product,
				categoryEntity, typeEntity);
		session.persist(entity);
		session.getTransaction().commit();
	}


	@Override
	public int countProduct(String code) {
		Query query = session
				.createQuery("select pi.amount from ProductEntity pi where productCode = :code");
		query.setParameter("code", code);
		return (Integer) query.uniqueResult();
	}

	@Override
	public List<Category> getCategories() {
		@SuppressWarnings("unchecked")
		List<Category> list = session
				.createQuery(
						"select new pl.lodz.p.kipme.bo.Category(ca.categoryCode, ca.name) from CategoryEntity ca")
				.list();
		return list;
	}

	private CategoryEntity getCategoryEntityByCode(String code) {
		Query createQuery = session
				.createQuery("from CategoryEntity where categoryCode = :code");
		createQuery.setParameter("code", code);
		return (CategoryEntity) createQuery.uniqueResult();
	}

	@Override
	public Product getProductByCode(String productCode) {
		Query createQuery = session
				.createQuery("from ProductEntity where productCode = :code");
		createQuery.setParameter("code", productCode);
		ProductEntity result = (ProductEntity) createQuery.uniqueResult();
		return result == null ? null : ProductUtil
				.productEntity2Product(result);
	}

	private ProductEntity getProductEntityByCode(String productCode) {
		Query createQuery = session
				.createQuery("from ProductEntity where productCode = :code");
		createQuery.setParameter("code", productCode);
		return (ProductEntity) createQuery.uniqueResult();
	}

	@Override
	public List<ProductInfo> getProductInfoList() {
		Query query = session.createQuery("select new pl.lodz.p.kipme.bo.ProductInfo(e.amount, e.name, e.productCode) from ProductEntity e");
		List<ProductInfo> list = query.list();
		return list;
	}

	@Override
	public List<Product> getProductsByCategory(String category) {
		Query createQuery = session
				.createQuery("from ProductEntity where category.categoryCode = :category");
		createQuery.setParameter("category", category);
		@SuppressWarnings("unchecked")
		List<ProductEntity> list = createQuery.list();
		List<Product> result = new ArrayList<Product>();
		for (ProductEntity productEntity : list) {
			result.add(ProductUtil.productEntity2Product(productEntity));
		}
		return result;
	}

	@Override
	public List<Product> getProductsByType(String type) {
		Query createQuery = session
				.createQuery("from ProductEntity where type.typeCode = :type");
		createQuery.setParameter("type", type);
		@SuppressWarnings("unchecked")
		List<ProductEntity> list = createQuery.list();
		List<Product> result = new ArrayList<Product>();
		for (ProductEntity productEntity : list) {
			result.add(ProductUtil.productEntity2Product(productEntity));
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<ProductType> getProductTypes() {
		String queryString = "select new pl.lodz.p.kipme.bo.ProductType(pt.typeCode, pt.name) from TypeEntity pt";
		@SuppressWarnings("rawtypes")
		List list = session.createQuery(queryString).list();
		return list;
	}

	private TypeEntity getTypeEntityByCode(String code) {
		Query createQuery = session
				.createQuery("from TypeEntity where typeCode = :code");
		createQuery.setParameter("code", code);
		return (TypeEntity) createQuery.uniqueResult();
	}

	@Override
	public void removeProduct(String productCode) {
		session.beginTransaction();
		ProductEntity entity = getProductEntityByCode(productCode);
		session.delete(entity);
		session.getTransaction().commit();
	}

	@Override
	public void updateProduct(Product product) throws BusinessException {
		session.beginTransaction();
		ProductEntity productEntity = getProductEntityByCode(product
				.getProductCode());
		if(productEntity == null){
			throw new BusinessException("Product with code: " + product.getProductCode() + " does not exists in DB");
		}
		ProductEntity mergeEntity = ProductUtil.mergeProductEntities(product,
				productEntity, getCategoryEntityByCode(product.getCategory().getCategoryCode()), 
				getTypeEntityByCode(product.getType().getTypeCode()));
		session.update(mergeEntity);
		session.getTransaction().commit();
	}


	@Override
	public void updateNumberOfProduct(String productCode, int newAmount) {
		session.beginTransaction();
		ProductEntity entity = getProductEntityByCode(productCode);
		entity.setAmount(entity.getAmount() + newAmount);
		session.update(entity);
		session.getTransaction().commit();
	}

}
